package com.dahua.messagesyncbj.service.Impl;

import com.dahua.messagesyncbj.config.redis.RedisUtil;
import com.dahua.messagesyncbj.criteria.FaultQueryCriteria;
import com.dahua.messagesyncbj.criteria.StatusQueryCriteria;
import com.dahua.messagesyncbj.entity.Fault;
import com.dahua.messagesyncbj.entity.Status;
import com.dahua.messagesyncbj.mapper.FaultMapper;
import com.dahua.messagesyncbj.pojo.EquipmentInfo;
import com.dahua.messagesyncbj.service.EquipmentService;
import com.dahua.messagesyncbj.service.FaultService;
import com.dahua.messagesyncbj.util.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;

import static com.dahua.messagesyncbj.business.Constants.FAULT_LIST;
import static com.dahua.messagesyncbj.business.Constants.RECOVER_LIST;

@Slf4j
@Service
public class FaultServiceImpl implements FaultService {
    @Resource
    private RedisUtil redisUtil;
    @Resource
    private RedissonClient redissonClient;
    @Resource
    private FaultMapper faultMapper;
    @Resource
    private EquipmentService equipmentService;

    /*  故障分级由大到小：一~五级*/
    @Override
    @Transactional
    public void insertFault(Fault fault, String sensorType) {
        log.info("【接收到报警信息】：{}", fault);
        try {
            LocalDateTime now = LocalDateTime.now();
            fault.setSensorType(sensorType);
            fault.setPartGroupId(100);

            EquipmentInfo info = equipmentService.enquire(fault.getManufactureId());

            /*  rank the grade of faults to five level*/
            /*int level = fault.getLevel();
            if (level == 1) {
                fault.setEventLevel(3);
            } else if (level > 2) {
                fault.setEventLevel(1);
            } else {
                fault.setEventLevel(fault.getLevel());
            }*/
            fault.setEventLevel(fault.getLevel());

            /*  continue operation if equipment is existed*/
            if (info != null) {
                /*  简化content*/
                fault.setSimpleContent(StringUtil.divided(fault.getContent()));

                fault.setLineId(info.getLineId());
                fault.setStationId(info.getStationId());
                fault.setEquipmentId(info.getId());
                fault.setEquipmentType(info.getBk2());
                fault.setBk5(info.getBk5());

                /*  if exist similar event already, drop it*/
                {
                    /*  redisson control several threads simultaneously in multithreading environment, all of which need to operate on fault table*/
                    RLock lock = redissonClient.getLock("fault." + fault.getManufactureId());
                    try {
                        lock.lock();
                        List<Fault> faults = getHistoryFaults(fault);
                        if (!faults.isEmpty()) {
                            log.warn("【当前设备已存在该故障信息】：{}", fault.getManufactureId());
                            return;
                        }
                        /*  insert*/
                        faultMapper.insertFaultList(Collections.singletonList(fault));
                    } catch (Exception e) {
                        log.error(e.getMessage());
                    } finally {
                        lock.unlock();
                    }
                }

                /*  update latest status of equipment*/
                RLock lock = redissonClient.getLock("status." + fault.getManufactureId());
                try {
                    lock.lock();
                    List<Status> statuses = equipmentService.listLatestStatus(new StatusQueryCriteria(fault.getManufactureId()));

                    Status status = new Status();
                    status.setLineId(info.getLineId());
                    status.setStationId(info.getStationId());
                    status.setEquipmentId(info.getId());
                    status.setManufactureId(fault.getManufactureId());
                    status.setEquipmentType(info.getBk2());
                    status.setLogTime(Timestamp.valueOf(now));
                    status.setStatus(fault.getEventLevel());
                    status.setStatusPoint(fault.getMeasurementId());
                    status.setSensorType(fault.getSensorType());
                    if (statuses.isEmpty()) {
                        /*  有时查不到50704的设备状态信息*/
                        log.warn("【查询不到当前设备状态信息】：{}", fault.getManufactureId());
                        log.warn("【当前报警】：{}", fault.toString());


                        equipmentService.insertLatestStatus(status);
                    } else {
                        if (statuses.get(0).getStatus() == 0) {
                            equipmentService.updateLatestStatus(status);
                        } else {
                            if (status.getLogTime().before(statuses.get(0).getLogTime())) {
                                status.setLogTime(statuses.get(0).getLogTime());
                            }
                            if (fault.getSensorType().charAt(0) > statuses.get(0).getSensorType().charAt(0)) {
                                equipmentService.updateLatestStatus(status);
                            } else if (fault.getSensorType().charAt(0) == statuses.get(0).getSensorType().charAt(0)) {

                                /*  fault becomes more important with grade lower*/
                                if (fault.getEventLevel() <= statuses.get(0).getStatus()) {
                                    equipmentService.updateLatestStatus(status);
                                }
                            }
                        }
                    }
                } catch (Exception e) {
                    log.error(e.getMessage());
                } finally {
                    lock.unlock();
                }

            }

            /*  give up insert data to fault table if even did not add this equipment*/
            /*else {
                //该设备信息未录入系统
                redisUtil.rightPush(FAULT_LIST, fault);
            }*/
        } catch (Exception e) {
            log.error(e.getMessage());
        }
    }

    @Override
    @Transactional
    public void recover(Fault fault) {
        log.info("【接收到恢复报警信息】：{}", fault);
        LocalDateTime now = LocalDateTime.now();

        /*  redisson control several threads simultaneously in multithreading environment, all of which need to operate on fault table*/
        RLock lock_fault = redissonClient.getLock("fault." + fault.getManufactureId());
        try {
            lock_fault.lock();
            List<Fault> faults = getHistoryFaults(fault);

            if (!faults.isEmpty()) {
                fault.setIsFixed(1);
                faultMapper.fixFault(fault);
                lock_fault.unlock();

                RLock lock = redissonClient.getLock("status." + fault.getManufactureId());
                try {
                    lock.lock();
                    /*  查找未修复的报警*/
                    lock_fault.lock();
                    FaultQueryCriteria criteria = new FaultQueryCriteria();
                    criteria.setManufactureId(fault.getManufactureId());
                    criteria.setIsFixed(0);
                    List<Fault> h_faults = faultMapper.listFaults(criteria);
                    lock_fault.unlock();

                    List<Status> statuses = equipmentService.listLatestStatus(new StatusQueryCriteria(fault.getManufactureId()));

                    if (h_faults.isEmpty()) {
                        if (statuses.size() == 1) {
                            if (statuses.get(0).getStatus() > 0) {
                                Status status = statuses.get(0);
                                status.setStatus(0);
                                status.setLogTime(Timestamp.valueOf(now));
                                status.setSensorType(null);
                                equipmentService.updateLatestStatus(status);
                            } else if (statuses.get(0).getStatus() == 0) {
                                return;
                            }
                        } else {
                            EquipmentInfo info = equipmentService.enquire(fault.getManufactureId());
                            if (info == null) {
                                return;
                            }
                            Status status = new Status();
                            status.setLineId(info.getLineId());
                            status.setStationId(info.getStationId());
                            status.setEquipmentId(info.getId());
                            status.setManufactureId(info.getName());
                            status.setEquipmentType(info.getBk2());
                            status.setLogTime(Timestamp.valueOf(now));
                            status.setStatus(0);
                            status.setStatusPoint(fault.getMeasurementId());
                            status.setSensorType(null);
                            equipmentService.insertLatestStatus(status);
                        }
                    } else {
                        Status status = getWorstStatus(h_faults);
                        if (statuses.size() == 1) {
                            Status status_h = statuses.get(0);
                            status_h.setStatus(status.getStatus());
                            status_h.setSensorType(status.getSensorType());
                            status_h.setLogTime(Timestamp.valueOf(now));
                            equipmentService.updateLatestStatus(status_h);
                        } else {
                            EquipmentInfo info = equipmentService.enquire(fault.getManufactureId());
                            if (info == null) {
                                return;
                            }
                            Status status_n = new Status();
                            status_n.setLineId(info.getLineId());
                            status_n.setStationId(info.getStationId());
                            status_n.setEquipmentId(info.getId());
                            status_n.setManufactureId(info.getName());
                            status_n.setEquipmentType(info.getBk2());

                            status_n.setLogTime(Timestamp.valueOf(now));
                            status_n.setStatus(status.getStatus());
                            status_n.setSensorType(status.getSensorType());

                            status_n.setStatusPoint(fault.getMeasurementId());
                            equipmentService.insertLatestStatus(status_n);
                        }
                    }
                } catch (Exception e) {
                    log.error(e.getMessage());
                } finally {
                    lock.unlock();
                }
            }else{
                log.warn("【当前设备没有报警信息】: {}", fault.getManufactureId());
            }

        } catch (Exception e) {
            log.error(e.getMessage());
        } finally {
            /*  unlock if it is not release lock voluntarily*/
            if (lock_fault.isLocked()){
                lock_fault.unlock();
            }
        }


        /*  ditch useless data*/
        /*else {
            EquipmentInfo info = equipmentService.enquire(fault.getManufactureId());
            if (info != null) {
                try {
                    redisUtil.rightPush(RECOVER_LIST, fault);
                } catch (Exception e) {
                    log.error(e.getMessage());
                }
            }
        }*/
    }

    private List<Fault> getHistoryFaults(Fault fault) {
        FaultQueryCriteria faultQueryCriteria = new FaultQueryCriteria();

        if (fault.getManufactureId() == null){
            throw new RuntimeException("manufactureId can not be null");
        }
        faultQueryCriteria.setManufactureId(fault.getManufactureId());

        if (fault.getMeasurementId() != null) {
            faultQueryCriteria.setMeasurementId(fault.getMeasurementId());
        }
        if (fault.getContent() != null) {
            faultQueryCriteria.setContent(fault.getContent());
        }
        faultQueryCriteria.setLevel(fault.getLevel());
        if (fault.getDescribe() != null) {
            faultQueryCriteria.setDescribe(fault.getDescribe());
        }
        faultQueryCriteria.setSensorType(fault.getSensorType());

        return faultMapper.listFaults(faultQueryCriteria);
    }

    private static Status getWorstStatus(List<Fault> h_faults) {
        char A = 65;
        int eventLevel_A = 0;
        int eventLevel_B = 0;
        for (Fault hFault : h_faults) {
            if (hFault.getSensorType().charAt(0) > 65) {
                if (hFault.getEventLevel() < eventLevel_B) {
                    eventLevel_B = hFault.getEventLevel();
                }
            } else {
                if (hFault.getEventLevel() < eventLevel_A) {
                    eventLevel_A = hFault.getEventLevel();
                }
            }

        }
        Status status = new Status();
        if (eventLevel_B != 0) {
            status.setSensorType("B");
            status.setStatus(eventLevel_B);
        } else {
            status.setSensorType("A");
            status.setStatus(eventLevel_A);
        }
        return status;
    }

    @Override
    @Transactional
    public void fixAllFault(String manufactureId, Long endTime) {
        log.info("【设备故障修复】：{}", manufactureId);

        Fault fault = new Fault();
        fault.setManufactureId(manufactureId);
        fault.setIsFixed(1);
        fault.setH_isFixed(0);
        if (endTime == null) {
            fault.setEndTime(Timestamp.valueOf(LocalDateTime.now()));
        } else {
            fault.setEndTime(new Timestamp(endTime));
        }

        /*  redisson control several threads simultaneously in multithreading environment, all of which need to operate on fault table*/
        RLock lock_fault = redissonClient.getLock("fault." + manufactureId);
        try {
            lock_fault.lock();

            faultMapper.fixFault(fault);

        }catch (Exception e){
            log.error(e.getMessage());
        }finally {
            lock_fault.unlock();
        }
    }
}
